package org.cli.admin.util;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.scannotation.AnnotationDB;
import org.scannotation.ClasspathUrlFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Binder;
import com.google.inject.multibindings.Multibinder;

public class PlugableUtil {
	private static final Logger logger = LoggerFactory
			.getLogger(PlugableUtil.class);

	private static <T> String arrayToString(T[] a, String separator) {
		StringBuffer result = new StringBuffer();
		if (a.length > 0) {
			result.append(a[0]);
			for (int i = 1; i < a.length; i++) {
				result.append(separator);
				result.append(a[i]);
			}
		}
		return result.toString();
	}

	public static <T extends Annotation, I> List<Class<? extends I>> findAndInstall(
			Binder binder, Class<T> annotationClass, Class<I> commonInterface) {
		URL[] urls = ClasspathUrlFinder.findClassPaths();

		logger.debug("Scanning classpath:" + arrayToString(urls, ","));

		List<Class<? extends I>> results = new LinkedList<Class<? extends I>>();
		try {
			AnnotationDB db = new AnnotationDB();
			db.setScanClassAnnotations(true);
			db.scanArchives(urls);

			Map<String, Set<String>> annotationIndex = db.getAnnotationIndex();

			Multibinder<I> multibinder = Multibinder.newSetBinder(binder,
					commonInterface);

			Set<String> commandClassNames = annotationIndex.get(annotationClass
					.getName());

			if (null == commandClassNames) {
				return results;
			}

			for (String commandClassName : commandClassNames) {

				try {
					Class<?> aClass = Class.forName(commandClassName);
					if (commonInterface.isAssignableFrom(aClass)) {

						@SuppressWarnings("unchecked")
						Class<? extends I> commandClass = (Class<? extends I>) aClass;

						multibinder.addBinding().to(commandClass);
						
						binder.bind(commandClass);
						results.add(commandClass);
					}
				} catch (ClassNotFoundException e) {
					logger.info("Could not find plugin class:"
							+ commandClassName);
				}
			}

		} catch (IOException e) {
			binder.addError("Exception when finding annotated plugins", e);
		}

		return results;
	}
}
